{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## 函数式编程初探\n",
    "\n",
    "诞生50多年之后，[函数式编程](https://en.wikipedia.org/wiki/Functional_programming)（functional programming）开始获得越来越多的关注。\n",
    "\n",
    "不仅最古老的函数式语言Lisp重获青春，而且新的函数式语言层出不穷，比如Erlang、clojure、Scala、F#等等。目前最当红的Python、Ruby、Javascript，对函数式编程的支持都很强，就连老牌的面向对象的Java、面向过程的PHP，都忙不迭地加入对匿名函数的支持。越来越多的迹象表明，函数式编程已经不再是学术界的最爱，开始大踏步地在业界投入实用。\n",
    "\n",
    "也许继\"面向对象编程\"之后，\"函数式编程\"会成为下一个编程的主流范式（paradigm）。未来的程序员恐怕或多或少都必须懂一点。\n",
    "\n",
    "![](res/imgs/img.png)\n",
    "\n",
    "但是，\"函数式编程\"看上去比较难，缺乏通俗的入门教程，各种介绍文章都充斥着数学符号和专用术语，让人读了如坠云雾。就连最基本的问题\"什么是函数式编程\"，网上都搜不到易懂的回答。\n",
    "\n",
    "下面是我的\"函数式编程\"学习笔记，分享出来，与大家一起探讨。内容不涉及数学（我也不懂[Lambda Calculus](https://en.wikipedia.org/wiki/Lambda_calculus)），也不涉及高级特性（比如[lazy evaluation](https://en.wikipedia.org/wiki/Lazy_evaluation)和[currying](https://en.wikipedia.org/wiki/Currying)），只求尽量简单通俗地整理和表达，我现在所理解的\"函数式编程\"以及它的意义。\n",
    "\n",
    "我主要参考了Slava Akhmechet的_[\"Functional Programming For The Rest of Us\"](http://www.defmacro.org/ramblings/fp.html)_。\n",
    "\n",
    "**一、定义**\n",
    "\n",
    "简单说，\"函数式编程\"是一种[\"编程范式\"](https://en.wikipedia.org/wiki/Programming_paradigm)（programming paradigm），也就是如何编写程序的方法论。\n",
    "\n",
    "它属于[\"结构化编程\"](https://en.wikipedia.org/wiki/Structured_programming)的一种，主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说，现在有这样一个数学表达式：\n",
    "\n",
    "> 　　(1 + 2) \\* 3 - 4\n",
    "\n",
    "传统的过程式编程，可能这样写：\n",
    "\n",
    "> 　　var a = 1 + 2;\n",
    ">\n",
    "> 　　var b = a \\* 3;\n",
    ">\n",
    "> 　　var c = b - 4;\n",
    "\n",
    "函数式编程要求使用函数，我们可以把运算过程[定义](http://lostechies.com/derickbailey/2012/01/24/some-thoughts-on-functional-javascript/)为不同的函数，然后写成下面这样：\n",
    "\n",
    "> 　　var result = subtract(multiply(add(1,2), 3), 4);\n",
    "\n",
    "这就是函数式编程。\n",
    "\n",
    "**二、特点**\n",
    "\n",
    "函数式编程具有五个鲜明的特点。\n",
    "\n",
    "**1\\. 函数是\"第一等公民\"**\n",
    "\n",
    "所谓[\"第一等公民\"](https://en.wikipedia.org/wiki/First-class_function)（first class），指的是函数与其他数据类型一样，处于平等地位，可以赋值给其他变量，也可以作为参数，传入另一个函数，或者作为别的函数的返回值。\n",
    "\n",
    "举例来说，下面代码中的print变量就是一个函数，可以作为另一个函数的参数。\n",
    "\n",
    "> 　　var print = function(i){ console.log(i);};\n",
    ">\n",
    "> 　　\\[1,2,3\\].forEach(print);\n",
    "\n",
    "**2\\. 只用\"表达式\"，不用\"语句\"**\n",
    "\n",
    "\"表达式\"（expression）是一个单纯的运算过程，总是有返回值；\"语句\"（statement）是执行某种操作，没有返回值。函数式编程要求，只使用表达式，不使用语句。也就是说，每一步都是单纯的运算，而且都有返回值。\n",
    "\n",
    "原因是函数式编程的开发动机，一开始就是为了处理运算（computation），不考虑系统的读写（I/O）。\"语句\"属于对系统的读写操作，所以就被排斥在外。\n",
    "\n",
    "当然，实际应用中，不做I/O是不可能的。因此，编程过程中，函数式编程只要求把I/O限制到最小，不要有不必要的读写行为，保持计算过程的单纯性。\n",
    "\n",
    "**3\\. 没有\"副作用\"**\n",
    "\n",
    "所谓[\"副作用\"](https://en.wikipedia.org/wiki/Side_effect_(computer_science))（side effect），指的是函数内部与外部互动（最典型的情况，就是修改全局变量的值），产生运算以外的其他结果。\n",
    "\n",
    "函数式编程强调没有\"副作用\"，意味着函数要保持独立，所有功能就是返回一个新的值，没有其他行为，尤其是不得修改外部变量的值。\n",
    "\n",
    "**4\\. 不修改状态**\n",
    "\n",
    "上一点已经提到，函数式编程只是返回新的值，不修改系统变量。因此，不修改变量，也是它的一个重要特点。\n",
    "\n",
    "在其他类型的语言中，变量往往用来保存\"状态\"（state）。不修改变量，意味着状态不能保存在变量中。函数式编程使用参数保存状态，最好的例子就是递归。下面的代码是一个将字符串逆序排列的函数，它演示了不同的参数如何决定了运算所处的\"状态\"。\n",
    "\n",
    "> 　　function reverse(string) {\n",
    ">\n",
    "> 　　　　if(string.length == 0) {\n",
    ">\n",
    "> 　　　　　　return string;\n",
    ">\n",
    "> 　　　　} else {\n",
    ">\n",
    "> 　　　　　　return reverse(string.substring(1, string.length)) + string.substring(0, 1);\n",
    ">\n",
    "> 　　　　}\n",
    ">\n",
    "> 　　}\n",
    "\n",
    "由于使用了递归，函数式语言的运行速度比较慢，这是它长期不能在业界推广的主要原因。\n",
    "\n",
    "**5\\. 引用透明**\n",
    "\n",
    "引用透明（Referential transparency），指的是函数的运行不依赖于外部变量或\"状态\"，只依赖于输入的参数，任何时候只要参数相同，引用函数所得到的返回值总是相同的。\n",
    "\n",
    "有了前面的第三点和第四点，这点是很显然的。其他类型的语言，函数的返回值往往与系统状态有关，不同的状态之下，返回值是不一样的。这就叫\"引用不透明\"，很不利于观察和理解程序的行为。\n",
    "\n",
    "**三、意义**\n",
    "\n",
    "函数式编程到底有什么好处，为什么会变得越来越流行？\n",
    "\n",
    "**1\\. 代码简洁，开发快速**\n",
    "\n",
    "函数式编程大量使用函数，减少了代码的重复，因此程序比较短，开发速度较快。\n",
    "\n",
    "Paul Graham在[《黑客与画家》](https://www.ruanyifeng.com/docs/pg/)一书中[写道](https://www.ruanyifeng.com/blog/2010/10/why_lisp_is_superior.html)：同样功能的程序，极端情况下，Lisp代码的长度可能是C代码的二十分之一。\n",
    "\n",
    "如果程序员每天所写的代码行数基本相同，这就意味着，\"C语言需要一年时间完成开发某个功能，Lisp语言只需要不到三星期。反过来说，如果某个新功能，Lisp语言完成开发需要三个月，C语言需要写五年。\"当然，这样的对比故意夸大了差异，但是\"在一个高度竞争的市场中，即使开发速度只相差两三倍，也足以使得你永远处在落后的位置。\"\n",
    "\n",
    "**2\\. 接近自然语言，易于理解**\n",
    "\n",
    "函数式编程的自由度很高，可以写出很接近自然语言的代码。\n",
    "\n",
    "前文曾经将表达式(1 + 2) \\* 3 - 4，写成函数式语言：\n",
    "\n",
    "> 　　subtract(multiply(add(1,2), 3), 4)\n",
    "\n",
    "对它进行变形，不难得到另一种写法：\n",
    "\n",
    "> 　　add(1,2).multiply(3).subtract(4)\n",
    "\n",
    "这基本就是自然语言的表达了。再看下面的代码，大家应该一眼就能明白它的意思吧：\n",
    "\n",
    "> 　　merge(\\[1,2\\],\\[3,4\\]).sort().search(\"2\")\n",
    "\n",
    "因此，函数式编程的代码更容易理解。\n",
    "\n",
    "**3\\. 更方便的代码管理**\n",
    "\n",
    "函数式编程不依赖、也不会改变外界的状态，只要给定输入参数，返回的结果必定相同。因此，每一个函数都可以被看做独立单元，很有利于进行单元测试（unit testing）和除错（debugging），以及模块化组合。\n",
    "\n",
    "**4\\. 易于\"并发编程\"**\n",
    "\n",
    "函数式编程不需要考虑\"死锁\"（deadlock），因为它不修改变量，所以根本不存在\"锁\"线程的问题。不必担心一个线程的数据，被另一个线程修改，所以可以很放心地把工作分摊到多个线程，部署\"并发编程\"（concurrency）。\n",
    "\n",
    "请看下面的代码：\n",
    "\n",
    "> 　　var s1 = Op1();\n",
    ">\n",
    "> 　　var s2 = Op2();\n",
    ">\n",
    "> 　　var s3 = concat(s1, s2);\n",
    "\n",
    "由于s1和s2互不干扰，不会修改变量，谁先执行是无所谓的，所以可以放心地增加线程，把它们分配在两个线程上完成。其他类型的语言就做不到这一点，因为s1可能会修改系统状态，而s2可能会用到这些状态，所以必须保证s2在s1之后运行，自然也就不能部署到其他线程上了。\n",
    "\n",
    "多核CPU是将来的潮流，所以函数式编程的这个特性非常重要。\n",
    "\n",
    "**5\\. 代码的热升级**\n",
    "\n",
    "函数式编程没有副作用，只要保证接口不变，内部实现是外部无关的。所以，可以在运行状态下直接升级代码，不需要重启，也不需要停机。[Erlang](https://en.wikipedia.org/wiki/Erlang_(programming_language))语言早就证明了这一点，它是瑞典爱立信公司为了管理电话系统而开发的，电话系统的升级当然是不能停机的。\n",
    "\n",
    "（完）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}